module nazo.feed;
/**
 * Feedを生成するAPI
 * History:
 *          0.0.0.1 バージョン付け開始
 * Authors: Nazo
 * Version: 0.0.0.1
 * License: Public Domain
 */
private{
  import nazo.date;
  import nazo.string;
  import std.zlib;
  import nazo.xhtml;
  import std.stdio;
}

alias std.string.toString toString;

enum FeedType{
  RSS,
  ATOM,
  SITEMAP,
}

///Feedを作成するクラス。
class Feed(FeedType type,double ver=0.0){
  string uri;
  string lang;
  string siteName;
  string siteDescription;
  this(string uri,string siteName,string siteDescription,string lang="en"){
    this.uri=uri;
    this.siteName=siteName;
    this.siteDescription=siteDescription;
    this.lang=lang;
  }
  private string floatToString(float f){
    string rv=.toString(f);
    if(rv.find(".")==-1)rv~=".0";
    return rv;
  }
  private FeedItem[] items;
  void addItem(FeedItem item){
    items~=item;
  }
  uint itemLength(){
    return items.length;
  }
  ///Bugs: Atomが適当
  ///See_Also: Atom - RSS改訂の試み,http://www.kanzaki.com/docs/sw/atom.html
  ///See_Also: The Atom Syndication Format 0.3 (PRE-DRAFT),http://www.mnot.net/drafts/draft-nottingham-atom-format-02.txt
  string getContent(){
    static if(type==FeedType.RSS){
      static if(ver==1.0){
       string rv=`<?xml version="1.0" encoding="UTF-8"?>`\n`<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="`~this.lang~`">`\n;
       rv~=`<channel rdf:about="`~this.uri~`">`\n;
       rv~=`<title>`~Escape.text(this.siteName)~`</title>`\n;
       rv~=`<description>`~Escape.text(this.siteDescription)~`</description>`\n;
       rv~="<link>"~this.uri~"</link>\n";
       rv~="<items>\n<rdf:Seq>\n";
       string content;
       foreach(FeedItem item;items){
         rv~=`<rdf:li rdf:resource="`~item.uri~`"/>`\n;
         content~=`<item rdf:about="`~item.uri~`">`\n;
         content~="<title>"~Escape.text(item.title)~"</title>\n";
         if(item.description.length>0)content~="<dc:description>"~Escape.text(item.description)~"</dc:description>\n";
         content~="<link>"~item.uri~"</link>\n";
         content~="<dc:date>"~.toISO8610String(item.date)~"</dc:date>\n";
         content~="</item>\n";
       }
       rv~="</rdf:Seq>\n</items>\n</channel>\n"~content~"</rdf:RDF>\n";
        return rv;
      }else static if(ver==0.91||ver==2.0){
        string rv=`<?xml version="1.0" encoding="UTF-8"?>`\n;
        if(ver==0.91)
          rv~=`<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd">`\n;
        rv~=`<rss version="`~floatToString(ver)~`">`\n`<channel>`\n;
        rv~="<title>"~Escape.text(this.siteName)~"</title>\n";
        rv~="<link>"~this.uri~"</link>\n";
        rv~="<description>"~Escape.text(this.siteDescription)~"</description>\n";
        rv~="<language>"~this.lang~"</language>\n";
        foreach(FeedItem item;items){
          rv~="<item>\n";
          rv~="<title>"~Escape.text(item.title)~"</title>\n";
          rv~="<link>"~item.uri~"</link>\n";
          if(ver==0.91){
            rv~="<description>"~std.date.toString(item.date)~"</description>\n";
          }else if(ver==2.0){
            if(item.description.length>0)rv~="<description>"~Escape.text(item.description)~"</description>\n";
            rv~="<pubDate>"~std.date.toString(item.date)~"</pubDate>\n";
          }
          rv~="</item>\n";
        }
        rv~="</channel>\n</rss>";
        return rv;
      }else static assert(0);
    }else static if(type==FeedType.ATOM){
      static if(ver==0.3||ver==0.0){
        string rv=`<?xml version="1.0" encoding="UTF-8"?>`\n`<feed`~(ver==0.3?` version="0.3"`:"")~` xmlns="`~(ver==0.3?"http://purl.org/atom/ns#":"http://www.w3.org/2005/Atom")~`" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en">`\n;
        rv~=`<title>`~this.siteName~`</title>`\n;
        if(ver==0.3){
          rv~=`<subtitle>`~Escape.text(this.siteDescription)~`</subtitle>`\n;
        }else{
          rv~=`<tagline>`~Escape.text(this.siteDescription)~`</tagline>`\n;
        }
        rv~="<id>tag:"~uri.replace("https://","").replace("http://","").getBefore('/')~","~.toString(cast(ulong)adler32(0,cast(void[])this.siteName/*XXX why void[]?*/),36u)~"</id>"\n;
        rv~=`<link`~(ver==0.3?` rel="alternate"`:``)~` type="application/xhtml+xml" href="`~this.uri~`"/>`\n;
        foreach(FeedItem item;items){
          rv~="<entry>\n";
          rv~="<title>"~Escape.text(item.title)~"</title>\n";
          if(ver==0.3){
            rv~="<modified>"~.toISO8610String(item.date)~"</modified>\n";
          }else{
            rv~="<updated>"~.toISO8610String(item.date)~"</updated>\n";
          }
          rv~="<id>tag:"~item.uri.replace("https://","").replace("http://","").getBefore('/')~","~.toString(cast(ulong)adler32(0,cast(void[])item.title/*XXX why void[]?*/),36u)~"</id>\n";
          rv~=`<link`~(ver==0.3?` rel="alternate"`:``)~` type="application/xhtml+xml" href="`~item.uri~`"/>`\n;
          if(item.description.length>0)rv~=`<summary type="text/plain">`~Escape.text(item.description)~"</summary>\n";
          rv~="</entry>\n";
        }
        rv~="</feed>";
        return rv;
      }else static assert(0);
    }else static if(type==FeedType.SITEMAP){
      string rv="<?xml version='1.0' encoding='UTF-8'?>"\n
                "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\""\n
                "        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\n
                "        xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\""\n
                "        url=\"http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"\n;
      foreach(FeedItem item;items){
        rv~="<url>\n";
        rv~="<loc>"~item.uri~"</loc>\n";
        rv~="<lastmod>"~.toISO8610String(item.date)~"</lastmod>\n";
        rv~="</url>\n";
      }
      return rv~"</urlset>";
    }else
      static assert(0);
  }
}

class FeedItem{
  string uri;
  string title;
  string description;
  d_time date;
  this(string uri,string title,d_time date,string description=""){
    this.uri=uri;
    this.title=title;
    this.date=date;
    this.description=description;
  }
}
